import sys

import pygame
from define import *
from  pygame.locals import *

# 生成推盘序列
def getStartingBoard():
    return [[1,4,7],[2,5,8],[3,6,BLANK]]

# 静态页面
def drawStaticWin():
    # 窗口
    WINSET = pygame.display.set_mode((WINWIDTH,WINHEIGHT))
    # 名字
    pygame.display.set_caption("华容道")
    # 背景图片
    image = pygame.image.load(".\\05-mvcPuzzly\\bg.jpg")
#     绘制到窗口
    WINSET.blit(image,(0,0))

#     新游戏按钮
    new_surf,new_rect = makeText("新游戏", BTCOLOR , BTTEXTCOLOR,WINWIDTH-85,WINHEIGHT-40)
#     绘制到窗口中
    WINSET.blit(new_surf,new_rect)
#     返回
    return WINSET,new_surf,new_rect

#创建字体图像并设置位置
def makeText(text,tColor,btColor,top,left):
	textSurf = BASICFONT.render(text,True,tColor,btColor)
	textRect = textSurf.get_rect()
	textRect.topleft = (top,left)
	return textSurf,textRect

# 动态页面
def drawBoard(board,msg):
    WINSET.blit(STATICSURF, (0, 0))
    # 提示信息
    if msg:
        msgSurf,msgRect = makeText(msg,MSGCOLOR,None,5,5)
        pygame.image.save(msgSurf,"msg.png")
        imageSurf = pygame.image.load("msg.png")
        WINSET.blit(imageSurf,msgRect)
#     绘制推盘
    for i in range(len(board)):
        for j in range(len(board[0])):
            if board[i][j]:
                # 绘制方块
                drawTile(i,j,board[i][j])
#     计算方块距离原点的横纵坐标
    left,top = getLeftTopOfTile(0,0)
    width = COL *BLOCKSIZE
    height = ROW * BLOCKSIZE
#     绘制外边框
    pygame.draw.rect(WINSET,BDCOLOR,(left-5,top-5,width+11,height+11),4)

#求推盘中的方块board[tilex,tiley)与窗口左和上的距离
def getLeftTopOfTile(tilex,tiley):
    # 拼图距离窗口边缘的距离
    xMargin = int((WINWIDTH - (BLOCKSIZE * COL + (COL - 1))) / 2)
    yMargin = int((WINHEIGHT - (BLOCKSIZE * ROW + (ROW - 1))) / 2)
    left = xMargin + (tilex * BLOCKSIZE) + (tilex - 1)
    top = yMargin + (tiley * BLOCKSIZE) + (tiley - 1)
    return left, top

#在窗口的坐标(tilex,tiley)处绘制拼图块
def drawTile(tilex,tiley,number,adjx=0,adjy=0):
	left,top = getLeftTopOfTile(tilex,tiley)
	#draw.rect(surface,线条或填充的颜色,((x,y),(width,height)))，绘制矩形
	pygame.draw.rect(WINSET,BTCOLOR,(left+adjx,top+adjy,BLOCKSIZE,BLOCKSIZE))
	textSurf = BASICFONT.render(str(number),True,BTTEXTCOLOR)
	textRect = textSurf.get_rect()
	textRect.center = left+int(BLOCKSIZE/2)+adjx,top+int(BLOCKSIZE/2)+adjy
	WINSET.blit(textSurf,textRect)

# 初始化
def generateNewPuzzle(numSlides):
    # 获取拼图的矩阵
    mainBoard = getStartingBoard()
#     绘制动态的推盘
    drawBoard(mainBoard,"")
#     记录上次的fangxiang
    lastMove = None
#     循环
    for i in range(numSlides):
#        获取每次随机移动的方向
        move = getRandomMove(mainBoard,lastMove)
#         实现本次移动的动画
        slideAnimation(mainBoard,move,"初始化。。。",int(BLOCKSIZE / 3))
#         修改推盘序列中元素的序列
        makeMove(mainBoard,move)
#         记录本次产生的移动方向
        lastMove = move

#     返回最终的序列
    return mainBoard

#交换拼图列表中元素的位置---------【processing】的子层
def makeMove(board,move):
	blankx,blanky = getBlankPosition(board)
	if move == UP:
		board[blankx][blanky],board[blankx][blanky+1] = board[blankx][blanky+1],board[blankx][blanky]
	elif move == DOWN:
		board[blankx][blanky],board[blankx][blanky-1] = board[blankx][blanky-1],board[blankx][blanky]
	elif move == LEFT:
		board[blankx][blanky],board[blankx+1][blanky] = board[blankx+1][blanky],board[blankx][blanky]
	elif move == RIGHT:
		board[blankx][blanky],board[blankx-1][blanky] = board[blankx-1][blanky],board[blankx][blanky]


# 幻灯片动画
def slideAnimation(board, direction, msg, animationSpeed):
    blankx, blanky = getBlankPosition(board)
    # 因为是以列为单位的，所以和按行加的不一样
    if direction == UP:
        movex = blankx
        movey = blanky + 1
    elif direction == DOWN:
        movex = blankx
        movey = blanky - 1
    elif direction == LEFT:
        movex = blankx + 1
        movey = blanky
    elif direction == RIGHT:
        movex = blankx - 1
        movey = blanky
    # 基础准备
    drawBoard(board, msg)
    BASESURF = WINSET.copy()  # surface.bilt()创建surface对象的复制
    # 在要移动的拼图块上绘制空白块
    moveLeft, moveTop = getLeftTopOfTile(movex, movey)
    pygame.draw.rect(BASESURF, BLANKCOLOR, (moveLeft, moveTop, BLOCKSIZE, BLOCKSIZE))

    # 在移动路径上连续绘制被移动的方块
    for i in range(0, BLOCKSIZE, animationSpeed):
        checkForQuit()
        WINSET.blit(BASESURF, (0, 0))
        if direction == UP:
            drawTile(movex, movey, board[movex][movey], 0, -i)
        if direction == DOWN:
            drawTile(movex, movey, board[movex][movey], 0, i)
        if direction == LEFT:
            drawTile(movex, movey, board[movex][movey], -i, 0)
        if direction == RIGHT:
            drawTile(movex, movey, board[movex][movey], i, 0)
        pygame.display.update()
        FPSCLOCK.tick(FPS)

#随机移动拼图
#最近一次移动方向为上，或者向下不是有效移动---------generateNewPuzzle的子层
def getRandomMove(board,lastMove=None):
	validMoves = [UP,DOWN,LEFT,RIGHT]
	#从列表中删除被取消资格的移动方向
	if lastMove == UP or not isValidMove(board,DOWN):
		validMoves.remove(DOWN)
	if lastMove == DOWN or not isValidMove(board,UP):
		validMoves.remove(UP)
	if lastMove == LEFT or not isValidMove(board,RIGHT):
		validMoves.remove(RIGHT)
	if lastMove == RIGHT or not isValidMove(board,LEFT):
		validMoves.remove(LEFT)
	return random.choice(validMoves)

#根据空白块的位置和移动的方向判断本次移动是否为有效移动----------genRandomMove()的子层
def isValidMove(board,move):
    blankx,blanky = getBlankPosition(board)
    if move == UP:
        return blanky != len(board[0])-1
    if move == DOWN:
        return blanky != 0
    if move == LEFT:
        return blankx != len(board)-1
    if move == RIGHT:
        return blankx != 0


# 接收输入
def getInput(mainBoard):
    # 获取所有的事件列表
    events = pygame.event.get()
#     定义变量，保存返回值
    userInput = None
#     事件处理循环
    for event in events:
        if event.type == MOUSEBUTTONUP:
#             根据鼠标的位置（x,y）坐标，获取方块在推盘中的位置
            spotx,spoty = getSpotClicked(mainBoard,event.pos[0],event.pos[1])
            # 新游戏
            if(spotx,spoty) == (None,None) and NEW_RECT.collidepoint(event.pos):
                userInput = NEWGAME
            else:
                if mainBoard == getStartingBoard():
                    break
#                 获取空白拼图块在拼图的位置
            blankx,blanky = getBlankPosition(mainBoard)
            if spotx == blankx+1 and spoty == blanky:
                userInput = LEFT
            elif spotx == blankx-1 and spoty == blanky:
                userInput = RIGHT
            elif spotx == blankx and spoty==blanky+1:
                userInput = UP
            elif spotx== blankx and spoty == blanky-1:
                userInput = DOWN
    return userInput

#获取空白拼图块在拼图中的位置
def getBlankPosition(board):
	for x in range(COL):
		for y in range(ROW):
			if board[x][y] == BLANK:
				return (x,y)

#根据x,y坐标，获取方块在拼图中的位置--------【getInput】的子层
def getSpotClicked(board,x,y):
	for tilex in range(len(board)):
		for tiley in range(len(board[0])):
			left,top = getLeftTopOfTile(tilex,tiley)
			tileRect = pygame.Rect(left,top,BLOCKSIZE,BLOCKSIZE)
			if tileRect.collidepoint(x,y):		#如果x,y在碎方块内
				return tilex,tiley				#返回方块坐标
	return None,None

# 输入处理
def processing(userInput,mainBoard,msg):
    # 判断游戏是否完成
    if mainBoard == getStartingBoard():
        msg="完成"
    else:
        msg="通过鼠标移动方块"

    if userInput:
#         功能按钮
        if userInput == NEWGAME:
            initBoard = getStartingBoard()
            mainBoard = generateNewPuzzle(AUTOMOVE)
        else:
            slideAnimation(mainBoard,userInput,msg,8)
            makeMove(mainBoard,userInput)
#     返回
    return mainBoard,msg


# 游戏退出
def checkForQuit():
    for event in pygame.event.get(QUIT):
        terminate()
    for event in pygame.event.get(KEYUP):
        if event.key == K_ESCAPE:
            terminate()
        pygame.event.post(event)

# 终止操作
def terminate():
    pygame.quit()
    sys.exit()

# main()函数组织程序结构
def main():
    # 全局
    global  WINSET, STATICSURF
    global NEW_SURF,NEW_RECT
    global BASICFONT,FPSCLOCK
    pygame.init()
    FPSCLOCK = pygame.time.Clock()
    BASICFONT = pygame.font.Font('.\\05-mvcPuzzly\\STKAITI.TTF', 25)  # 字体
    WINSET ,NEW_SURF,NEW_RECT= drawStaticWin()
    initBoard = getStartingBoard()
    STATICSURF = WINSET.copy()
    mainBoard = generateNewPuzzle(AUTOMOVE)
#     提示信息
    msg = None
#     游戏循环
    while True:
        # 动态界面绘制
        drawBoard(mainBoard,msg)
        pygame.display.update()
        checkForQuit()
#         获取输入
        userInput = getInput(mainBoard)
        mainBoard,msg = processing(userInput,mainBoard,msg)
#         游戏时钟
        FPSCLOCK.tick(60)


if __name__ == '__main__':
    main()